package com.fakeDYChat.common.user.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import com.fakeDYChat.common.common.annotation.RedissonLock;
import com.fakeDYChat.common.common.domain.enums.YesOrNoEnum;
import com.fakeDYChat.common.user.dao.UserBackpackDao;
import com.fakeDYChat.common.user.domain.entity.UserBackpack;
import com.fakeDYChat.common.user.domain.enums.IdempotentEnum;
import com.fakeDYChat.common.user.service.IUserBackpackService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.Optional;

@Service
public class IUserBackpackServiceImpl implements IUserBackpackService {

    public static final String ACQUIRE_ITEM = "acquireItem_";
    private final UserBackpackDao userBackpackDao;

    @Autowired
    private ApplicationContext applicationContext;
    public IUserBackpackServiceImpl(UserBackpackDao userBackpackDao) {
        this.userBackpackDao = userBackpackDao;
    }

    /**
     * 用户获取一个物品
     * 通过Spring上下文获取IUserBackpackServiceImpl代理对象，
     * 然后再调用发放物品逻辑，是为了避免同类调用问题。
     * 此处的同类调用问题主要是该类中的某个方法需要Spring事务管理，所以打上了@Transactional注解来实现，
     * 但是如果再acquireItem方法中直接调用doAcquireItem方法的话，会导致事务失效。主要原因是不是通过
     * 依赖注入的IUserBackpackServiceImpl代理类来调用doAcquireItem方法，而是通过直接的方法调用，
     * 这就导致了本该在代理A中完成对象B的逻辑，但是现在直接在对象B中完成，并没有通过代理A完成，导致无法
     * 进行事务操作。
     *
     * @param uid            用户id
     * @param itemId         物品id
     * @param idempotentEnum 幂等类型
     * @param businessId     业务id
     * @author Tang
     * @date 2024/1/3 15:54
     **/
    @Override
    public void acquireItem(Long uid, Long itemId, IdempotentEnum idempotentEnum, String businessId) {
        //组装幂等号
        String idempotent = getIdempotent(itemId, idempotentEnum, businessId);
        //执行发放逻辑
        Optional.ofNullable(SpringUtil.getBean(IUserBackpackServiceImpl.class)).ifPresent(i -> {
            i.doAcquireItem(uid, itemId, idempotent);
        });
    }

    /**
     * 发放物品逻辑
     *
     * @param uid        用户id
     * @param itemId     物品id
     * @param idempotent 幂等号
     * @author Tang
     * @date 2024/1/4 14:36
     **/
    @Transactional(rollbackFor = Exception.class)
    @RedissonLock(key = "#idempotent", waitTime = 5000)
    public void doAcquireItem(Long uid, Long itemId, String idempotent) {
        //获取对应的物品
        UserBackpack item = userBackpackDao.getByIdem(idempotent);
        //如果背包有幂等号对应的物品，则不进行发放
        if (Objects.nonNull(item)) {
            return;
        }
        //发放物品
        UserBackpack newItem = UserBackpack.builder()
                .uid(uid)
                .itemId(itemId)
                .status(YesOrNoEnum.NO.getStatus())
                .idempotent(idempotent)
                .build();
        userBackpackDao.save(newItem);
    }

    /**
     * 组装幂等号
     *
     * @param itemId         物品id
     * @param idempotentEnum 幂等类型
     * @param businessId     业务id
     * @return java.lang.String
     * @author Tang
     * @date 2024/1/3 16:05
     **/
    private String getIdempotent(Long itemId, IdempotentEnum idempotentEnum, String businessId) {
        return String.format(ACQUIRE_ITEM + "%d_%d_%s", itemId, idempotentEnum.getType(), businessId);
    }

}
